<?php

define('BASE_PATH', realpath(dirname(__FILE__) . '/../../../'));
define('APPLICATION_PATH', BASE_PATH . DIRECTORY_SEPARATOR . 'application');

/**
 * Description of ShemaProvider
 *
 * @category Core
 * @package  Core_Tool
 *
 * @version $Id$
 */
class Core_Tool_SchemaProvider extends Zend_Tool_Project_Provider_Abstract
{

    protected $_migrationFolder = null;

    /**
     *
     * @var Zend_Db_Adapter_Interface
     */
    protected $_db = null;

    /**
     * Constructor
     */
    public function  __construct() {
        parent::__construct();

        $this->_migrationFolder = dirname($this->_getConfigPath()) . '/migrations';
    }

    /**
     * Generate Model, ModelRow, ModelTable and DbTable
     * @param string $module        ModuleName
     * @param string $name          ModelName
     * @param string $realTableName Table name
     */
    public function generateModels($name, $realTableName, $module)
    {
        $this->_init();
        $this->_getDbAdapter();
        $columns = $this->_db->describeTable($realTableName);
        
        $classModelName    = ucfirst($module) . '_Model_' . ucfirst($name);
        $classModelDbTable = ucfirst($module) . '_Model_DbTable_' . ucfirst($name);
        $modelPath         = APPLICATION_PATH . '/modules/' . $module . '/models/';

        //Generate Model
        
        $model = $this->_generateModel($classModelName, 'Core_Model_Abstract');
        $docblock = $this->_generateDocBlock(
            'Definition set and get methods to data obtained/transmitted in the table'
        );

        foreach($columns as $column=>$params) {
            $propertyName = '_' . $column;
            $model->setProperty(array(
                'name'         => $propertyName,
                'visibility'   => 'protected'
            ));
            $model->setMethods(array(
                array(
                    'name'       => 'set' . ucfirst($column),
                    'visibility' => 'public',
                    'parameter'  => array('name' => $column),
                    'body'       => '$this->' . $propertyName . ' = $' . $column . ';'
                ),
                array(
                    'name'       => 'get' . ucfirst($column),
                    'visibility' => 'public',
                    'body'       => 'return $this->' . $propertyName . ';'
                )
            ));
        }

        $this->_generateFile($model, $modelPath . ucfirst($name), $docblock);

        //Generate ModelMapper
        $model = $this->_generateModel($classModelName . 'Mapper', 'Core_Model_Mapper_Abstract');
        $model->setProperty(array(
            'name'             => '_dbName',
            'visibility'       => 'protected',
            'defaultValue'     => $classModelDbTable
        ));
        $docblock->setShortDescription('Set methods for use operation in dbtable.');
        $this->_generateFile($model, $modelPath . ucfirst($name) . 'Table', $docblock);
    }

    /**
     * Generate migration file
     *
     * @param string $nameMigration Name migration file
     */
    public function generateMigration($nameMigration = 'migration')
    {
        $migrationVersion = 0;

        $d = dir($this->_migrationFolder);
        while (false !== ($entry = $d->read())) {
            if (preg_match('/^([0-9]+)\-(.*)\.php/i', $entry, $matches) ) {
                if ($migrationVersion < $matches[1]) {
                    $migrationVersion = $matches[1];
                }
            }
        }

        $migrationFile = $this->_migrationFolder . '/' . ++$migrationVersion . '-' . $nameMigration;

        $model = $this->_generateModel(ucfirst($nameMigration), 'Core_Db_Schema_Change');
        $model->setMethods(array(
            array(
                'name' => 'up',
                'visibility' => 'public'
            ),
            array(
                'name' => 'down',
                'visibility' => 'public'
            )
        ));
        $docblock = $this->_generateDocBlock('Migration data');
        $this->_generateFile($model, $migrationFile, $docblock);
    }

    public function migration($version = null)
    {
        $this->_init();
        $this->_getDbAdapter();

        try {
            $manager = new Core_Db_Schema_Manager($this->_dir, $this->_db);
            $result = $manager->updateTo($version);

            switch ($result) {
                case Core_Db_Schema_Manager::RESULT_AT_CURRENT_VERSION:
                    if (!$version) {
                        $version = $manager->getCurrentSchemaVersion();
                    }
                    $this->_print("Already at version $version");
                    break;

                case Core_Db_Schema_Manager::RESULT_NO_MIGRATIONS_FOUND :
                    $this->_print("No migration files found to migrate from {$manager->getCurrentSchemaVersion()} to $version");
                    break;

                default:
                    $this->_print(
                        'Updated to version ' . $manager->getCurrentSchemaVersion(),
                        array('color' => 'green')
                    );
            }

            return true;
        } catch (Core_Db_Schema_Exception $e) {
            $this->_print('AN ERROR HAS OCCURED:', array('color' => 'red'));
            $this->_print($e->getMessage());
            return false;
        }
    }

    /**
     * Show current migration number
     *
     * @return bool
     */
    public function showMigration()
    {
        $this->_init();

        try {
            // Initialize and retrieve DB resource
            $db = $this->_getDbAdapter();
            $manager = new Core_Db_Schema_Manager($dir, $db);
            $this->_print('Current schema version is ' . $manager->getCurrentSchemaVersion());
            return true;
        } catch (Core_Db_Schema_Exception $e) {
            $this->_print('AN ERROR HAS OCCURED:'. array('color' => 'red'));
            $this->_print($e->getMessage());
            return false;
        }
    }

    public function dumpSql()
    {

    }

    public function loadSql()
    {
        
    }

    /**
     * Retrieve initialized DB connection
     *
     * @return Zend_Db_Adapter_Interface
     */
    protected function _getDbAdapter()
    {
        if ((null === $this->_db)) {
            $dbConfig = $this->_config->resources->db;
            $this->_db = Zend_Db::factory($dbConfig->adapter, $dbConfig->params);
        }
        return $this->_db;
    }

    /**
     * Generate model class
     *
     * @param string $name     Class name
     * @param string $extended Extended class
     * @param string $property Property with class
     * @return Zend_CodeGenerator_Php_Class
     */
    protected function _generateModel($name, $extended = null, $property = null)
    {
        $model = new Zend_CodeGenerator_Php_Class();
        $model->setName($name);

        if (null != $extended) {
            $model->setExtendedClass($extended);
        }

        if (null != $property) {
            $model->setProperty($property);
        }
        
        return $model;
    }

    /**
     * Generate php file
     * @param string $model
     * @param string $filePath
     * @param string $docblock
     */
    protected function _generateFile($model, $filePath, $docblock)
    {
        $filePath .= '.php';

        if (!file_exists($filePath)) {
            $fileModel = new Zend_CodeGenerator_Php_File();
            $fileModel->setClass($model);
            $fileModel->setFilename($filePath);
            $fileModel->setDocblock($docblock);

            $fileModel->write();
            $this->_print('Generate successful ' . $filePath, array('color' => 'green'));
        }
    }

    /**
     * Generate docblock
     *
     * @param string $shortDescription
     * @param string $module
     * @param string $subpackage
     * @return Zend_CodeGenerator_Php_Docblock
     */
    protected function _generateDocBlock($shortDescription)
    {
        return new Zend_CodeGenerator_Php_Docblock(array(
            'shortDescription' => $shortDescription,
            'longDescription'  => 'This is a class generated with Zend_CodeGenerator.',
            'tags'             => array(
                array(
                    'name'        => 'category',
                    'description' => '###CATEGORY###'
                ),
                array(
                    'name'        => 'package',
                    'description' => '###PACKAGE###'
                ),
                array(
                    'name'        => 'subpackage',
                    'description' => '###SUBPACKAGE###'
                ),
                array(
                    'name'        => 'version',
                    'description' => '$Id$',
                ),
                array(
                    'name'        => 'license',
                    'description' => 'New BSD',
                ),
            )
        ));
    }

    /**
     * Initialize config
     */
    protected function _init()
    {
        $appConfigFilePath = $this->_getConfigPath();
        $this->_config = new Zend_Config_Ini($appConfigFilePath, 'production', true);

        $developmentConfigPath = dirname($appConfigFilePath) . '/application.development.ini';
        if (file_exists($developmentConfigPath)) {
            $configOther = new Zend_Config_Ini($developmentConfigPath, 'development');
            $this->_config->merge($configOther);
        }
    }

    /**
     * Get path to config file
     * @return string
     */
    protected function _getConfigPath()
    {
        $profile = $this->_loadProfile(self::NO_PROFILE_THROW_EXCEPTION);
        $appConfigFileResource = $profile->search('applicationConfigFile');
        if ($appConfigFileResource == false) {
            throw new Zend_Tool_Project_Exception('A project with an application config file is required to use this provider.');
        }
        return $appConfigFileResource->getPath();
    }

    /**
     * @param string $line
     * @param array $decoratorOptions
     */
    protected function _print($line, array $decoratorOptions = array())
    {
        $this->_registry->getResponse()->appendContent("[Schema] " . $line, $decoratorOptions);
    }
}
